package com.duguo.dynamicmvc.factory.processor.view;

import java.util.List;
import java.util.Map;

import javassist.CtClass;
import javassist.CtMethod;

import com.duguo.dynamicmvc.factory.ControllerBuilder;
import com.duguo.dynamicmvc.factory.processor.AbstractProcessor;
import com.duguo.dynamicmvc.factory.processor.controller.ControllerNameBuilder;
import com.duguo.dynamicmvc.factory.utils.BeanUtils;
import com.duguo.dynamicmvc.view.View;
import com.duguo.dynamicmvc.view.redirect.RedirectToParentView;
import com.duguo.dynamicmvc.view.resolver.ViewResolver;

public class ViewProcessorImpl extends AbstractProcessor implements ViewProcessor{
	
	ViewAnnotationReader _viewAnnotationReader;
	ControllerNameBuilder _controllerNameBuilder;
	ViewResolver _viewResolver;

	public String getControllerName(CtMethod controllerCtMethod) throws Exception{
		return _viewAnnotationReader.getControllerName(controllerCtMethod);
	}

	public List<String> getInterceptors(CtMethod controllerCtMethod) throws Exception{
		return _viewAnnotationReader.getInterceptors(controllerCtMethod);
	}

	public boolean hasView(CtClass controllerCtClass, String viewName,ControllerBuilder controllerBuilder) throws Exception{
		return _viewAnnotationReader.hasView(controllerCtClass,viewName,controllerBuilder);
	}

	public void bindView(Object ctrlInstance,String controllerPath,Map<String, Object> staticViewMapping,CtMethod controllerMethodCtMethod, boolean isSingleAction)throws Exception {
        View view = getSuccessView(controllerPath,staticViewMapping, controllerMethodCtMethod, isSingleAction);
        BeanUtils.setFieldValue(ctrlInstance, "view", view);
        if(BeanUtils.hasField(ctrlInstance, "validationExceptionView")){
        	view=getValidationExceptionViewForMehtod(controllerPath,staticViewMapping, controllerMethodCtMethod);
            BeanUtils.setFieldValue(ctrlInstance, "validationExceptionView", view);
        }
	}
	
	public void bindView(Object parentController,Object ctrlInstance, View view) {
        BeanUtils.setFieldValue(ctrlInstance, "view", view);
        if(BeanUtils.hasField(ctrlInstance, "validationExceptionView")){
        	view=getValidationExceptionViewForView(parentController);
            BeanUtils.setFieldValue(ctrlInstance, "validationExceptionView", view);
        }
	}

    public View getViewByName(String controllerPath,String defaultName,boolean ignoreNotfound) {
    	View view=_viewResolver.resolve(controllerPath+defaultName);
    	if(view==null){
    		if(!defaultName.startsWith("/") && defaultName.indexOf(":")<0){
    			defaultName=controllerPath+defaultName;
    		}
    		view=_viewResolver.resolve(defaultName);
    	}
    	return  view;
    }



    private View getSuccessView(String controllerPath,Map<String, Object> staticViewMapping,CtMethod controllerMethodCtMethod, boolean isSingleAction)throws Exception {
    	View view = getViewFromViewMapping(controllerPath,staticViewMapping,controllerMethodCtMethod.getName());
    	if(view==null){
	    	String defaultName=_viewAnnotationReader.getSuccessViewName(controllerMethodCtMethod);
	    	if(isSingleAction && defaultName.endsWith("index")) {
	    		defaultName=controllerPath.substring(0,controllerPath.length()-1);
	    	}
	    	if(_viewAnnotationReader.isSuccessViewRedirect(controllerMethodCtMethod)){
	    		view = getRedirectView(controllerPath, defaultName);
	    	}else{
	    		view = getViewByName(controllerPath,staticViewMapping,defaultName,false);
	    	}
    	}
    	if(view==null)
    		throw new RuntimeException("Cannot find view for:"+controllerPath+controllerMethodCtMethod.getName());
        return view;
    }

	private View getRedirectView(String controllerPath, String defaultName) {
		View view;
		view= new RedirectToParentView();
		if(defaultName.equals("doDelete")){
			((RedirectToParentView)view).setLevel(2);
		}else if(!_controllerNameBuilder.isDo("/"+defaultName)){
			int level=1;
			while(defaultName.startsWith("../")){
				defaultName=defaultName.substring(3);
				level++;
			}
			if(defaultName.length()==0)
				defaultName="index";	    				
			getViewByName(controllerPath,defaultName,false);
			((RedirectToParentView)view).setViewName(defaultName);
			((RedirectToParentView)view).setLevel(level);
		}
		return view;
	}

    View getValidationExceptionViewForView(Object parentController) {
    	Map controllers=BeanUtils.getFieldValue(parentController, "registedControllers", Map.class);
    	if(controllers.containsKey("")){
    		return BeanUtils.getFieldValue(controllers.get(""), "view", View.class);
    	}else{
    		String currentControllerPath=_controllerNameBuilder.getControllerPath(parentController.getClass().getName());
    		View view=_viewResolver.resolve(currentControllerPath+"index");
        	if(view !=null)
        		return view;
    	}

    	parentController=_controllerNameBuilder.getParentController(parentController);
    	if(parentController==null)
    		throw new RuntimeException("Cannot find view for ValidationException");
    	
        return getValidationExceptionViewForView(parentController);
    }

    View getValidationExceptionViewForMehtod(String controllerPath,Map<String, Object> staticViewMapping,CtMethod controllerMethodCtMethod) throws Exception{
    	View view =getInputViewFromAnnotation(controllerPath,staticViewMapping,controllerMethodCtMethod);
    	if(view==null){
    		view =getDoMethodDefaultInputView(controllerPath,staticViewMapping,controllerMethodCtMethod);
    		while(view==null && controllerPath.length()>1){
	        	view = getViewByName(controllerPath,"index",true);
	        	controllerPath=_controllerNameBuilder.getParentLevelName(controllerPath);
	        }
    	}
    	if(view==null)
    		throw new RuntimeException("Cannot find view for ValidationException for:"+controllerPath+controllerMethodCtMethod.getName());
        return view;
    }

	private View getDoMethodDefaultInputView(String controllerPath,Map<String, Object> staticViewMapping,CtMethod controllerMethodCtMethod) {
		String defaultName=controllerMethodCtMethod.getName();
		if(_controllerNameBuilder.isDo("/"+defaultName)){
			defaultName=_controllerNameBuilder.getDoDefaultViewName(defaultName);	
			return getViewByName(controllerPath,staticViewMapping,defaultName,true);
		}
		return null;
	}


    private View getInputViewFromAnnotation(String controllerPath,Map<String, Object> staticViewMapping,CtMethod controllerMethodCtMethod)  throws Exception{
    	String viewName=_viewAnnotationReader.getInputViewName(controllerMethodCtMethod);
    	if(viewName!=null){
    		return getViewByName(controllerPath,staticViewMapping,viewName,false);
    	}
		return null;
	}

	private View getViewByName(String controllerPath,Map<String, Object> staticViewMapping, String viewName,boolean ignoreNotfound) {
		View view =getViewFromViewMapping(controllerPath,staticViewMapping,viewName);
		if(view==null){
			return getViewByName(controllerPath,viewName,ignoreNotfound);
		}
        return null;
    }

	private View getViewFromViewMapping(String controllerPath, Map<String, Object> staticViewMapping, String viewName) {
		if(staticViewMapping!=null){
			Object tempObj=staticViewMapping.get(viewName);
			if(tempObj!=null){
				if(tempObj instanceof String){
					return getViewByName(controllerPath,(String)tempObj,false);
				}else if(tempObj instanceof View){
					return (View)tempObj;
				}else{
					throw new RuntimeException("unknown view bean:"+tempObj);
				}
			}
		}
		return null;
	}

	public void setViewAnnotationReader(ViewAnnotationReader viewAnnotationReader) {
		_viewAnnotationReader = viewAnnotationReader;
	}

	public void setControllerNameBuilder(ControllerNameBuilder controllerNameBuilder) {
		_controllerNameBuilder = controllerNameBuilder;
	}

	public void setViewResolver(ViewResolver viewResolver) {
		_viewResolver = viewResolver;
	}
    
}
